/**
 * mycpu front end
 */

module frontend(
  input clk,
  input rst,

  // backend
  input        backend_redirect_taken,
  input        backend_redirect_is_br,
  input [31:0] backend_redirect_pc   ,
  input [31:0] backend_redirect_ds_pc,
  input        de_flush              ,
  input        de_stall              ,

  input        backend_branch          ,
  input        backend_branch_taken    ,
  input [31:0] backend_branch_pc       ,
  input [31:0] backend_branch_target_pc,

  output        valid_o   ,
  output        valid_next_o,
  output [31:0] pc_o      ,
  output [31:0] instr_o   ,
  output        ft_tlbre_o,
  output        ft_tlbi_o ,
  output        ft_adel_o ,

  output        br_pre_taken_o,
  output [31:0] br_pre_pc_o   ,

  // mmu
  output [31:0] instr_vaddr,
  output instr_vaddr_valid,
  output itlb0_flush,
  output itlb0_stall,

  input [31:0] instr_paddr      ,
  input        instr_paddr_valid,
  input        instr_uncache    ,
  input        instr_tlb_refill ,
  input        instr_tlb_invalid,
  
  // inst sram interface
  output inst_sram_req    ,
  input  inst_sram_addr_ok,

  output        inst_sram_uncache,
  output [31:0] inst_sram_addr   ,

  input         inst_sram_data_ok,
  input [  3:0] inst_sram_rstrb  ,
  input [127:0] inst_sram_rdata
);
  wire        pc_valid_o ;
  wire [31:0] pc_pc_o    ;

  wire [65:0] branch_info = {
    backend_branch,
    backend_branch_pc,
    backend_branch_taken,
    backend_branch_target_pc
  };
  wire [32:0] pred_result;
  wire [1:0] br_offset;

  wire ilw_stall_o;
  wire ilw_ft_adel_i = pc_pc_o[0] | pc_pc_o[1];
  wire ilw_valid_o;
  wire [31:0] ilw_pc_o;
  wire [127:0] ilw_instr_o;
  wire [  3:0] ilw_rstrb_o;
  wire ilw_br_pre_taken_o;
  wire [31:0] ilw_br_pre_pc_o;
  wire [1:0] ilw_br_offset_o;
  wire ilw_ft_tlbre_o;
  wire ilw_ft_tlbi_o;
  wire ilw_ft_adel_o;

  wire iq_stall_o;
  
  assign instr_vaddr = pc_pc_o;

  pc mycpu_pc(
    .clk     (clk     ),
    .rst     (rst     ),

    .frontend_redirect_taken(pred_result[32]),
    .frontend_redirect_pc(pred_result[31:0]),

    .backend_redirect_taken(backend_redirect_taken),
    .backend_redirect_pc   (backend_redirect_pc   ),
    .pc_stall(ilw_stall_o),

    .instr_vaddr_valid(instr_vaddr_valid),
    .instr_vaddr(instr_vaddr),
    .itlb0_flush(itlb0_flush),
    .itlb0_stall(itlb0_stall),

    .instr_paddr_valid(instr_paddr_valid),

    .valid_o (pc_valid_o ),
    .vaddr_o (pc_pc_o    )
  );

  bpu mycpu_bpu(
    .clk(clk),
    .rst(rst),

    .pc(pc_pc_o),

    .branch_info(branch_info), // pc[32bit] + br_taken [1bit] + br_target [32bit]
    .pred_result(pred_result),  //br_taken [1bit] + br_target [32bit]
    .br_offset(br_offset)
  );

  isram_like_wrap mycpu_isram_like_wrap(
    .clk(clk),
    .rst(rst),

    .valid_i(pc_valid_o),
    .stall_o(ilw_stall_o),

    .paddr_i   (instr_paddr      ),
    .vaddr_i   (pc_pc_o          ),
    .br_pre_taken_i(pred_result[32]),
    .br_pre_pc_i(pred_result[31:0]),
    .br_offset_i(br_offset),
    // .br_pre_taken_i(1'b0),
    // .br_pre_pc_i(pred_result[31:0]),
    .uncache_i (instr_uncache    ),
    .ft_tlbre_i(instr_tlb_refill ),
    .ft_tlbi_i (instr_tlb_invalid),
    .ft_adel_i (ilw_ft_adel_i    ),

    .valid_o (ilw_valid_o           ),
    .stall_i (iq_stall_o            ),
    .flush_i (de_flush              ),
    .br_taken(backend_redirect_is_br),
    .br_ds_pc(backend_redirect_ds_pc),
    
    .pc_o      (ilw_pc_o      ),
    .instr_o   (ilw_instr_o   ),
    .rstrb_o   (ilw_rstrb_o ),
    .br_pre_taken_o(ilw_br_pre_taken_o),
    .br_pre_pc_o(ilw_br_pre_pc_o),
    .br_offset_o(ilw_br_offset_o),
    .ft_tlbre_o(ilw_ft_tlbre_o),
    .ft_tlbi_o (ilw_ft_tlbi_o ),
    .ft_adel_o (ilw_ft_adel_o ),

    .inst_sram_req    (inst_sram_req    ),
    .inst_sram_addr_ok(inst_sram_addr_ok),
    .inst_sram_uncache(inst_sram_uncache),
    .inst_sram_addr   (inst_sram_addr   ),

    .inst_sram_data_ok(inst_sram_data_ok),
    .inst_sram_rstrb  (inst_sram_rstrb  ),
    .inst_sram_rdata  (inst_sram_rdata  )
  );

  instr_queue_wrap mycpu_instr_queue(
    .clk      (clk       ),
    .rst      (rst       ),

    .de_stall (de_stall  ),
    .de_flush (de_flush  ),

    .valid_i  (ilw_valid_o),
    .stall_o  (iq_stall_o ),

    .pc_i      (ilw_pc_o      ),
    .instr_i   (ilw_instr_o   ),
    .rstrb_i   (ilw_rstrb_o ),
    .br_pre_taken_i(ilw_br_pre_taken_o),
    .br_pre_pc_i(ilw_br_pre_pc_o),
    .br_offset_i(ilw_br_offset_o),
    .ft_tlbre_i(ilw_ft_tlbre_o),
    .ft_tlbi_i (ilw_ft_tlbi_o ),
    .ft_adel_i (ilw_ft_adel_o ),

    .pc_o          (pc_o      ),
    .instr_o       (instr_o   ),
    .br_pre_taken_o(br_pre_taken_o),
    .br_pre_pc_o(br_pre_pc_o),
    .valid_o       (valid_o   ),
    .valid_next_o  (valid_next_o),
    .ft_tlbre_o    (ft_tlbre_o),
    .ft_tlbi_o     (ft_tlbi_o ),
    .ft_adel_o     (ft_adel_o )
  );

  // TODO: no predictor is equal to predict not taken
  // assign br_pre_taken_o = 1'b0;
  // assign br_pre_pc_o    = 32'b0;

endmodule